package com.xuecheng.search.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.xuecheng.commons.model.dto.CoursePubIndexDto;
import com.xuecheng.commons.model.dto.PageRequest;
import com.xuecheng.commons.model.vo.CoursePubIndexVo;
import com.xuecheng.commons.model.vo.CoursePubVo;
import com.xuecheng.commons.model.vo.PageResponseResult;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.commons.utils.BeanHelper;
import com.xuecheng.content.api.ContentClient;
import com.xuecheng.search.domain.CoursePubIndex;
import com.xuecheng.search.service.SearchService;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.security.auth.kerberos.KerberosKey;
import java.io.IOException;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private ContentClient contentClient;

    @Autowired
    private RestHighLevelClient client;

    // 同步索引库
    @Override
    public void saveDocument(Long coursePubId) throws IOException {
        // 1.查询课程发布详情
        ResponseResult<CoursePubVo> responseResult = contentClient.findById(coursePubId);
        // 判断是否查询超过
        if (responseResult.getCode() == 0) {
            // 取出课程发布对象
            CoursePubVo coursePubVo = responseResult.getData();
            // 2.vo转索引实体
            CoursePubIndex coursePubIndex = BeanHelper.copyProperties(coursePubVo, CoursePubIndex.class);
            // 下划线命名需要单独指定
            coursePubIndex.setCourse_id(coursePubVo.getCourseId());
            coursePubIndex.setCompany_id(coursePubVo.getCompanyId());
            coursePubIndex.setCreate_date(new Date());
            coursePubIndex.setIs_pub(coursePubVo.getIsPub());
            coursePubIndex.setIs_latest(coursePubVo.getIsLatest());
            // 指定课程价格
            Long price = coursePubVo.getPrice();
            coursePubIndex.setPrice(price.floatValue());
            // 3.创建request
            IndexRequest request = new IndexRequest("course").id(coursePubId.toString());
            // 4.准备DSL
            request.source(JSON.toJSONString(coursePubIndex), XContentType.JSON);
            // 5.发送请求
            client.index(request, RequestOptions.DEFAULT);
        }
    }

    // 课程搜索
    @Override
    public ResponseResult search(PageRequest params, CoursePubIndexDto dto) throws IOException {
        // 1.创建搜索request
        SearchRequest request = new SearchRequest("course");
        // 2.准备DSL
        buildDSL(params, dto, request);
        // 3.client发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.解析结果并相应
        return parseResponse(response);
    }

    // 根据id查询
    @Override
    public ResponseResult findById(String id) throws IOException {
        // 1.创建查询request
        GetRequest request = new GetRequest("course").id(id);
        // 2.发送请求
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        // 3.解析响应
        String json = response.getSourceAsString();
        CoursePubIndexVo vo = JSON.parseObject(json, CoursePubIndexVo.class);
        vo.setIndexId(vo.getCourse_id()); // 课程id =  课程发布id  = 索引id
        // 返回结果
        return ResponseResult.okResult(vo);
    }

    // 2.准备DSL
    private void buildDSL(PageRequest params, CoursePubIndexDto dto, SearchRequest request) {
        // 2-1 query
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 关键字
        String keyword = dto.getKeyword();
        if (StrUtil.isBlank(keyword)) { // 查所有
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            boolQuery.must(QueryBuilders.matchQuery("all", keyword));
        }
        // 大分类
        String mt = dto.getMt();
        if (StrUtil.isNotBlank(mt)) {
            boolQuery.filter(QueryBuilders.termQuery("mt", mt));
        }
        // 小分类
        String st = dto.getSt();
        if (StrUtil.isNotBlank(st)) {
            boolQuery.filter(QueryBuilders.termQuery("st", st));
        }
        // 课程等级
        String grade = dto.getGrade();
        if (StrUtil.isNotBlank(grade)) {
            boolQuery.filter(QueryBuilders.termQuery("grade", grade));
        }

        request.source().query(boolQuery);
        // 2-2 分页
        Integer pageNo = params.getPageNo();
        Integer pageSize = params.getPageSize();
        Integer index = (pageNo - 1) * pageSize;
        request.source().from(index).size(pageSize);

        // 2-3 高亮
        request.source().highlighter(new HighlightBuilder()
                .field("name")
                .preTags("<font color=red>")
                .postTags("</font>")
                .requireFieldMatch(false));
    }

    // 4.解析结果并相应
    private ResponseResult parseResponse(SearchResponse response) {
        // 4-1 获取搜索命中结果
        SearchHits searchHits = response.getHits();
        // 4-2 取出总记录数
        long total = searchHits.getTotalHits().value;
        // 4-3 取出结果集数组
        List<CoursePubIndexVo> voList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        if (ArrayUtil.isNotEmpty(hits)) {
            // 4-4 遍历
            for (SearchHit hit : hits) {
                // 取出json结果
                String json = hit.getSourceAsString();
                // 转为java对象
                CoursePubIndexVo vo = JSON.parseObject(json, CoursePubIndexVo.class);
                vo.setIndexId(vo.getCourse_id()); // 设置索引id

                // 处理高亮
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if (CollectionUtil.isNotEmpty(highlightFields)) {
                    HighlightField nameField = highlightFields.get("name");
                    if (ArrayUtil.isNotEmpty(nameField)) {
                        String name = nameField.getFragments()[0].toString();
                        // 将高亮字段重置到vo
                        vo.setName(name);
                    }
                }

                // 添加到voList
                voList.add(vo);
            }
        }
        // 返回结果
        return PageResponseResult.okResult(total, voList);
    }
}
